﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
#if !SILVERLIGHT && !NETFX_CORE
using System.Linq;
using System.Text;
#endif

namespace DaveSexton.Labs
{
	public sealed class AnonymousTraceListener : TraceListener
	{
		#region Public Properties
		#endregion

		#region Private / Protected
		private readonly Action<TraceEventCache, string, TraceEventType, int, string> trace;
		private readonly Action<string> write, writeLine;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousTraceListener" /> class.
		/// </summary>
		public AnonymousTraceListener(
			Action<string> write,
			Action<string> writeLine)
			: this(string.Empty, write, writeLine)
		{
			Contract.Requires(write != null);
			Contract.Requires(writeLine != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousTraceListener" /> class.
		/// </summary>
		public AnonymousTraceListener(
			Action<string> write,
			Action<string> writeLine,
			Action<TraceEventCache, string, TraceEventType, int, string> trace)
			: this(string.Empty, write, writeLine, trace)
		{
			Contract.Requires(write != null);
			Contract.Requires(writeLine != null);
			Contract.Requires(trace != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousTraceListener" /> class.
		/// </summary>
		public AnonymousTraceListener(
			string name,
			Action<string> write,
			Action<string> writeLine)
			: this(name, write, writeLine, (eventCache, source, eventType, id, message) => writeLine(message))
		{
			Contract.Requires(name != null);
			Contract.Requires(write != null);
			Contract.Requires(writeLine != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousTraceListener" /> class.
		/// </summary>
		public AnonymousTraceListener(
			string name,
			Action<string> write,
			Action<string> writeLine,
			Action<TraceEventCache, string, TraceEventType, int, string> trace)
			: base(name)
		{
			Contract.Requires(name != null);
			Contract.Requires(write != null);
			Contract.Requires(writeLine != null);
			Contract.Requires(trace != null);

			this.write = write;
			this.writeLine = writeLine;
			this.trace = trace;
		}
		#endregion

		#region Methods
		[ContractInvariantMethod]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
		private void ObjectInvariant()
		{
			Contract.Invariant(trace != null);
			Contract.Invariant(write != null);
			Contract.Invariant(writeLine != null);
		}

#if !SILVERLIGHT && !NETFX_CORE
		public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
		{
			if (Filter == null || Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
			{
				trace(eventCache, source, eventType, id, (data == null) ? string.Empty : data.ToString());
			}
		}

		public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
		{
			if (Filter == null || Filter.ShouldTrace(eventCache, source, eventType, id, null, null, null, data))
			{
				var message = data == null
					? string.Empty
					: data.Aggregate(
							new StringBuilder(),
							(acc, cur) => acc.Append(cur).Append(", "),
							acc => (acc.Length == 0) ? string.Empty : acc.ToString(0, acc.Length - 2));

				trace(eventCache, source, eventType, id, message);
			}
		}
#endif

		public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
		{
#if !SILVERLIGHT && !NETFX_CORE
			if (Filter == null || Filter.ShouldTrace(eventCache, source, eventType, id, message ?? string.Empty, null, null, null))
#endif
			{
				trace(eventCache, source, eventType, id, message ?? string.Empty);
			}
		}

		public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
		{
#if !SILVERLIGHT && !NETFX_CORE
			if (Filter == null || Filter.ShouldTrace(eventCache, source, eventType, id, format ?? string.Empty, args, null, null))
#endif
			{
				if (format == null || args == null)
				{
					trace(eventCache, source, eventType, id, format ?? string.Empty);
				}
				else
				{
					trace(eventCache, source, eventType, id, string.Format(CultureInfo.InvariantCulture, format, args));
				}
			}
		}

		public override void Write(string message)
		{
			write(message ?? string.Empty);
		}

		public override void WriteLine(string message)
		{
			writeLine(message ?? string.Empty);
		}
		#endregion
	}
}